package foo.bar.helloworld;

import java.awt.Color;
import java.awt.image.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.imageio.ImageIO;

public class ImageUtil {
	public enum BrightnessType {
		SimpleBrightness, MediumBrightness, ComplexBrightness;
	}
	
	/**
	 * Метод для зчитування зображення за вказаним шляхом.
	 * @param fileName шлях до зображення.
	 * @return вказане зображення.
	 */
	public static BufferedImage loadImage(String fileName) {
		BufferedImage img = null;
		try {
			img = ImageIO.read(new File(fileName));
			return img;
		}
		catch (FileNotFoundException e) 
		{
			System.out.println("Файл \"" + fileName + "\" не знайдено!");
			e.printStackTrace();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * Метод для запису зображення за вказаним шляхом.
	 * @param image зображення.
	 * @param destination шлях (ім'я файлу) запису.
	 * @return true при успішному виконанні операції, інакше false.
	 */
	public static boolean saveImage(BufferedImage image, String destination) {
		try {
			ImageIO.write(image, destination.substring(destination.length() - 3), new File(destination));
			return true;
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		catch (NullPointerException e)
		{
			e.printStackTrace();
			System.out.println("Ссилка на зображення = null!");
		}
		return false;		
	}
	
	/**
	 * Метод для знаходження інтегральної матриці зображення.
	 * @param image зображення, інтегральну матрицю якого ми знаходимо.
	 * @return інтегральну матрицю зображення.
	 */
	public static int[][] getIntegralImage(BufferedImage image) {
		int height = image.getHeight();
		int width = image.getWidth();
		
		int[][] result = new int[height][width];
		int[][] bright = getBrightnessMatrix(image, BrightnessType.MediumBrightness);
		
		result[0][0] = bright[0][0];
		for(int i = 1; i < height; i++)
			result[i][0] = bright[i][0] + result[i-1][0];
		for(int j = 1; j < width; j++)
			result[0][j] = bright[0][j] + result[0][j-1];
		
		for(int i = 1; i < height; i++)
			for(int j = 1; j < width; j++)
				result[i][j] = bright[i][j] + result[i-1][j] + result[i][j-1] - result[i-1][j-1];
		
		return result;
	}
		
	/**
	 * Метод для знаходження матриці яскравостей пікселів зображення (за sRGB перетворенням).
	 * @param image зображення.
	 * @param brType тип алгоритму знаходження яскравості пікселя.
	 * @return матрицю яскравості пікселів зображення.
	 */
	public static int[][] getBrightnessMatrix(BufferedImage image, BrightnessType brType) {
		int height = image.getHeight();
		int width = image.getWidth();
		int[][] result = new int[height][width];
		Color c;
		double r;
		double g;
		double b;
		
		//"ВАЖКИЙ" спосіб розрахунку яскравості
		if(brType == BrightnessType.ComplexBrightness) {
			for (int i = 0; i < height; i++)
				for (int j = 0; j < width; j++) {
					c = new Color(image.getRGB(j, i));
					r = c.getRed();
					g = c.getGreen();
					b = c.getBlue();
					r =  Math.pow(r/255.0, 2.2);
					g =  Math.pow(g/255.0, 2.2);
					b =  Math.pow(b/255.0, 2.2);
					result[i][j] = (int) Math.round(255 * Math.pow(
																	0.21 * r +  
																	0.72 * g +
																	0.07 * b
																			, 1/2.2));
				}
		}
		//"СЕРЕДНІЙ"
		else if(brType == BrightnessType.MediumBrightness) {
			for (int i = 0; i < height; i++)
				for (int j = 0; j < width; j++) {
					c = new Color(image.getRGB(j, i));
					r = c.getRed();
					g = c.getGreen();
					b = c.getBlue();
					result[i][j] = (int) Math.round(0.21*r + 0.72*g + 0.07*b);
				}
		} 
		// "ПРОСТИЙ"
		else if(brType == BrightnessType.SimpleBrightness) { 
			for (int i = 0; i < height; i++)
				for (int j = 0; j < width; j++) {
					c = new Color(image.getRGB(j, i));
					r = c.getRed();
					g = c.getGreen();
					b = c.getBlue();
					result[i][j] = (int) Math.round(0.3*r + 0.59*g + 0.11*b);
				}
		}
		return result;		
	}
		
	/**
	 * Повертає матрицю з RGB кольорами зображення.
	 * @param image вихідне зображення.
	 * @return матриця, елементами якої є RGB значення відповідних кольорів.
	 */
	public static int[][] getIntMatrix(BufferedImage image) {
		int height = image.getHeight();
		int width = image.getWidth();
		int[][] result = new int[height][width];
		
		for (int i = 0; i < height; i++)
			for (int j = 0; j < width; j++)
				result[i][j] = image.getRGB(j, i);
		
		return result;
	}
	
	/**
	 * Створює зображення за заданою матрицею значень кольорів пікселів.
	 * @param colorsRGB матриця кольорів.
	 * @return створене зображення.
	 */
	public static BufferedImage makeImage(int[][] colorsRGB) {
		int height = colorsRGB.length;
		int width = colorsRGB[0].length;
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		
		for(int i = 0; i < height; i++)
			for(int j = 0; j < width; j++)
				image.setRGB(j, i, colorsRGB[i][j]);
		return	image;	
	}
	
	/**
	 * Метод для отримання зображення у відтінках сірого із даного.
	 * @param image перетворюване зображення.
	 * @return зображення у градаціях сірого.
	 */
	public static BufferedImage makeGray(BufferedImage image, BrightnessType brType) {
		int height = image.getHeight();
		int width = image.getWidth();
		int[][] temp = getBrightnessMatrix(image, brType);
		
		for (int i = 0; i < height; i++)
			for (int j = 0; j < width; j++)
				temp[i][j] = temp[i][j]<<16 | temp[i][j]<<8 | temp[i][j];
		
		return makeImage(temp);
	}
	
	public static BufferedImage differenceBetweenInt(BufferedImage image1, BufferedImage image2, float scale) {
		int height = image1.getHeight();
		int width = image1.getWidth();
		
		if((height == image2.getHeight()) & (width == image2.getWidth())) {
			int[][] i1 = getIntMatrix(image1);
			int[][] i2 = getIntMatrix(image2);
						
			for (int i = 0; i < height; i++)
				for (int j = 0; j < width; j++)
					i1[i][j] = Math.round(Math.abs(i1[i][j] - i2[i][j])*scale);
			return makeImage(i1);
		}
		return null;		
	}
	
	public static BufferedImage differenceBetweenRGB(BufferedImage image1, BufferedImage image2, float scale) {
		int height = image1.getHeight();
		int width = image1.getWidth();
		
		if((height == image2.getHeight()) & (width == image2.getWidth())) {
			
			int[][] im1 = getIntMatrix(image1);
			int[][] im2 = getIntMatrix(image2);
			int[][] res = new int[height][width];
						
			for (int i = 0; i < height; i++)
				for (int j = 0; j < width; j++)
					res[i][j] = Math.round( Math.abs( (im1[i][j] & 0xff0000) - (im2[i][j] & 0xff0000) ) * scale) << 16 |
								Math.round( Math.abs( (im1[i][j] & 0x00ff00) - (im2[i][j] & 0x00ff00) ) * scale) << 8 |
								Math.round( Math.abs( (im1[i][j] & 0x0000ff) - (im2[i][j] & 0x0000ff) ) * scale);
			return makeImage(res);
			
			// Не працює(((
			/*
			Color c1, c2;
			BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			
			for (int i = 0; i < height; i++)
				for (int j = 0; j < width; j++) {
					c1 = new Color(image1.getRGB(j, i));
					c2 = new Color(image2.getRGB(j, i));
					image.setRGB(j, i, new Color(
													Math.abs(c1.getRed() - c2.getRed())/255, 
													Math.abs(c1.getGreen() - c2.getGreen())/255, 
													Math.abs(c1.getBlue() - c2.getBlue())/255
												).getRGB()
								);
				}
			return image;	
			*/
		}
		return null;		
	}
	
}
